/*************************************************************************
	> File Name: htsocket.c
	> Author: xuwenlong
	> Mail: myxuan475@126.com
	> Created Time: 2018年01月18日 星期四 14时26分44秒
 ************************************************************************/

#include <stdio.h>
#include <fcntl.h>
#include "htsocket.h"
#include <errno.h>

namespace downlink {
SocketConnect::SocketConnect(const dbms::DeviceChannel_t &dc)
	: DownlinkConnect(dc)
	, socket_(dc.socket())
	, socketFd_(-1)
{
}

SocketConnect::~SocketConnect() { Close(); }

const std::string SocketConnect::Name() const
{
	return socket_.ip() + ":" + std::to_string(socket_.port());
}

int32_t SocketConnect::Handle() { return socketFd_; }

bool SocketConnect::IsAvailable()
{
	int timeout = 1000;
	return Available(timeout) > 0;
}

bool SocketConnect::ReConnect()
{
	Close();

	socketFd_ = Open();
	if (socketFd_ < 0) {
		return false;
	}

	return true;
}

void SocketConnect::ClearWriteBuffer() {}

void SocketConnect::ClearReadBuffer()
{
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(socketFd_, &fds);
	struct timeval tm;
	tm.tv_sec = 1;
	tm.tv_usec = 0;
	int32_t nRet = select(FD_SETSIZE, &fds, NULL, NULL, &tm);
	if (nRet == 0)
		return;
	char *tmp = (char *)malloc(nRet + 1);
	recv(socketFd_, tmp, nRet, 0);
	free(tmp);
}

bool SocketConnect::Send(const uint8_t cmd[], int32_t len)
{
	return send(socketFd_, (const char *)cmd, len, 0) > 0;
}

int32_t SocketConnect::Recv(uint8_t cmd[], int32_t max, int32_t timeout)
{
	return recv(socketFd_, cmd, max, 0);
#if 0
	int32_t recvLen = 0;
	int32_t ret = 0;

	if (timeout < 500)
		timeout = 500;
	int32_t byte = 0;
	while ((ret = Available(timeout)) > 0) {

		if ((recvLen = recv(socketFd_, (char *)(&cmd[byte]), ret, 0)) > 0) {
			byte += recvLen;
			recvLen = 0;
			if (byte >= max || timeout <= 0) {
				HTELINK_LOG_DEBUG(
					"recv limit, %d,%s clear", errno, strerror(errno));
				break;
			}
		} else {
			if (errno == 0) {
				return byte > 0;
			} else if (errno == EINTR) {
				HTELINK_LOG_DEBUG(
					"recv failed, %d,%s break", errno, strerror(errno));
				break;
			} else if (errno == EAGAIN) {
				HTELINK_LOG_DEBUG(
					"recv again, %d,%s continue", errno, strerror(errno));
				continue;
			} else {
				HTELINK_LOG_ERR(
					"recv failed, %d,%s break", errno, strerror(errno));
				break;
			}
		}
		if (timeout <= 0)
			break;
	}
	return byte > 0;
#endif
}

int32_t SocketConnect::Open()
{
	if (socketFd_ > 0) {
		Close();
	}
	struct sockaddr_in remote_addr;

	memset(&remote_addr, 0, sizeof(remote_addr)); // 数据初始化--清零
	remote_addr.sin_family = AF_INET;			  // 设置为IP通信
	remote_addr.sin_addr.s_addr =
		inet_addr(socket_.ip().c_str());		  // 服务器IP地址
	remote_addr.sin_port = htons(socket_.port()); // 服务器端口号

	/* 创建客户端套接字--IPv4协议，面向连接通信，TCP协议 */
	if ((socketFd_ = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
		return socketFd_;
	}

	/* 将套接字绑定到服务器的网络地址上 */
	if (connect(socketFd_, (struct sockaddr *)&remote_addr,
			sizeof(struct sockaddr)) < 0) {
		HTELINK_LOG_ERR("%s", strerror(errno));
		Close();
		return -1;
	}

	int fl = fcntl(socketFd_, F_GETFL, 0);
	fcntl(socketFd_, F_SETFL, fl | O_NONBLOCK);

	return socketFd_;
}

void SocketConnect::Close()
{
	if (socketFd_ != -1) {
		close(socketFd_);
		socketFd_ = -1;
	}
}

int32_t SocketConnect::Available(int &timeout)
{
	fd_set fd;
	int ret;

	FD_ZERO(&fd);
	FD_SET(socketFd_, &fd);

	timeval tm;
	tm.tv_sec = timeout / 1000;
	tm.tv_usec = (timeout % 1000) * 1000;

	ret = select(socketFd_ + 1, &fd, NULL, NULL, &tm);

	timeout = tm.tv_sec * 1000 + tm.tv_usec / 1000;

	if (ret < 0) {
		HTELINK_LOG_DEBUG("receive error:%d, %s", ret, strerror(errno));
		Close();
		return ret;
	} else if (ret == 0) {
		return ret;
	}

	if (FD_ISSET(socketFd_, &fd)) {
		return ret;
	}
	return 0;
}

void SocketConnect::CloseConnect() { Close(); }
} // namespace downlink
